// Script handler -- I wanna set this up the script ahead of time, cause 
// it is easier.  :D

import java.util.*;
import java.io.*;

public class Scripter {

   long		start; 
	
   private Vector	chute;

   public int     userList[];

   public int	numberItems;
   public String	name;
   public int	users;
   public int 	chutes;
   public int	errors;

   Scripter () {

      chute 	= new Vector(10000, 1000);   // Wild-assed capacity guesses
	numberItems = 0;
	name 		= null;
   }

   public String load(String  text) {

	ScriptItem    item;
	String	  param;

	int		  index  		= 0;
	int 		  textLineNum	= 1;	
	boolean	  error;

	boolean	  gotName		= false;
	boolean	  gotChute		= false;
	boolean	  fatalError	= false;

	Integer	  i;

	int		  tempParam;

	// For the tokenizer
	StringBuffer  a;
	StringBuffer  b;
	StringBuffer  c;
	char		  temp;
	int		  size;
	int		  rover;   
	int		  tc;

	StringBuffer report = new StringBuffer();

	// read as a stream until empty
	StringReader   s 	    = new StringReader(text);
	BufferedReader source = new BufferedReader(s); 
	String	   dest   = new String();

	Hashtable	   jumpList = new Hashtable();

	errors	   = 0;
	
	report.append("LOADING.\n");
	try {
	   dest = source.readLine();
	   while ((dest != null)&&(fatalError == false)) {	// will it null for an empty line? hmmm
		error = false;
		if ((dest.length() > 1)&&(dest.charAt(0)!= ScriptItem.commentChar)) {
			item    = new ScriptItem();

			// Convert the line into string tokens.
			size     = dest.length();
			rover    = 0;
			tc	   = 0;
			a	   = new StringBuffer();
			b	   = new StringBuffer();
			c	   = new StringBuffer(); 
			while (rover < size) {
        		   temp = dest.charAt(rover);
			   if (temp == (char)ScriptItem.tokenDelimit) tc++;
	   		   else {
            		switch (tc) {
					case 0: a.append(temp); break;
					case 1: b.append(temp); break;
					case 2: c.append(temp); break;
					default:  //for now, we will let extra delimiters be ignored.
			      }
	   		   }		
	   		   rover++;
			}

			item.token = toToken(a.toString());

			try {
				item.text = c.toString();
			} catch (Exception e) {
			}	

			// special processing for each token
			switch (item.token) {
			   case ScriptItem.tokenNone:
				 error = true;
				 errors++;
				 report.append("Could not parse line #" + textLineNum + "\n");
				 report.append("  : " + dest + "\n");
				 break;

			   case ScriptItem.tokenName:
				 if (gotName == true) {
				    report.append("FATAL ERROR:  Only on NAME allowed per script.\n");
				    fatalError = true;
				    break;	
				 }
				 name  = item.text;
				 try {
				    users = Integer.parseInt(b.toString());
				 } catch (Exception e) {
				    report.append("Could not parse USER # in line #" + textLineNum + "\n");
				    error = true;
				    errors++;
				    break;	
				 }
				 userList = new int[users+1];
				 for(rover=0; rover <= users; rover++) {userList[rover]=0;};
				 gotName = true;	 
				 break;

			   case ScriptItem.tokenChute:
				 if (gotChute == true) {
				    report.append("FATAL ERROR:  Only on CHUTE allowed per script.\n");
				    fatalError = true;
				    break;	
				 }
				 if (gotName == false) {
				    report.append("FATAL ERROR:  Must specify NAME before CHUTE.\n");
				    fatalError = true;
				    break;	
				 }
				 try {
				    chutes = Integer.parseInt(b.toString());
				 } catch (Exception e) {
				    report.append("Could not parse CHUTE # in line #" + textLineNum + "\n");
				    error = true;
				    errors++;
				    break;	
				 }
				 gotChute = true;	 
				 break;
	
			   case ScriptItem.tokenPut:
				 try {
				    item.id = Integer.parseInt(b.toString());
				 } catch (Exception e) {
				    item.id = 0;
				 }
				 chute.addElement(item);
			       numberItems++; 
				 break;

			   case ScriptItem.tokenIndexPut:
				 chute.addElement(item);
			       numberItems++; 
				 break;

			   case ScriptItem.tokenWait:
				 try {
				    item.count = Integer.parseInt(b.toString());
				 } catch (NumberFormatException eN) {
				    report.append("Could not parse WAIT value in line #" + textLineNum + "\n");
				    error = true;
				    errors++;
				    break;	
				 }
				 try {
				    item.id = Integer.parseInt(item.text);
				 } catch (NumberFormatException eN) {
				    item.id = 0;
				 }	
			   	 chute.addElement(item);
			       numberItems++; 
				 break;

			   case ScriptItem.tokenLoop:
				 try {
				    item.count = Integer.parseInt(b.toString());
			   	    chute.addElement(item);
				    jumpList.put(item.text, new Integer(numberItems));
			          numberItems++; 
				 } catch (NumberFormatException eN) {
				    report.append("Could not parse LOOP COUNT value in line #" + textLineNum + "\n");
				    error = true;
				    errors++;
				    break;
				 }
				 break;

			   case ScriptItem.tokenEndLoop:
				 try {
				    i=(Integer)jumpList.get(item.text);
				    if (i == null) {
				       report.append("Unknown loop symbol in line #" + textLineNum + "\n");
				       error = true;
				       errors++;
				    } else {
				       item.count   = i.intValue();	
			   	       chute.addElement(item);
			             numberItems++; 
				    }
				 } catch (Exception em) {
					report.append("General symbol error in line #" + textLineNum + "\n");
				      error = true;
				      errors++;
				 }
				 break;

			   case ScriptItem.tokenSync:
				 try {
				    item.count = Integer.parseInt(b.toString());
			   	    chute.addElement(item);
			          numberItems++; 
				 } catch (NumberFormatException eN) {
				    report.append("Could not parse SYNC value in line #" + textLineNum + "\n");
				    error = true;
				    errors++;
				    break;
				 }
				 try {
				    item.id = Integer.parseInt(item.text);
				 } catch (NumberFormatException eN) {
				    item.id = 0;
				 }	
				 break;

			   case ScriptItem.tokenIndexSet:
				 try {
				    item.count = Integer.parseInt(b.toString());
				 } catch (Exception e) {
				    report.append("Could not parse INDEX SET value in line #" + textLineNum + "\n");
				    error = true;
				    errors++;
				    break;	
				 }
				 chute.addElement(item);
			       numberItems++; 
				 break;


			   case ScriptItem.tokenIndexInc:
			   case ScriptItem.tokenEnd:
				 // Dont have to do anything
			   	 chute.addElement(item);
			       numberItems++; 
				 break;

			   case ScriptItem.tokenIndexWait:
				 try {
				    item.count = Integer.parseInt(b.toString());
				 } catch (NumberFormatException eN) {
				    report.append("Could not parse INDEX WAIT value in line #" + textLineNum + "\n");
				    error = true;
				    errors++;
				    break;	
				 }
			   	 chute.addElement(item);
			       numberItems++; 
				 break;

			   case ScriptItem.tokenAssign:
				 try {
				   size  = Integer.parseInt(b.toString());
				   rover = Integer.parseInt(c.toString());
				   if (rover > chutes) {
				      report.append("Not a valid chute in line #" + textLineNum + "\n");
				      error = true;
				      errors++;
				      break;	
				   }
 				   userList[size] = rover;
				 } catch (Exception e) {
				    report.append("Assign out of order or out of bounds in line #" + textLineNum + "\n");
				    error = true;
				    errors++;
				    break;	
				 }
				 break;
	
			   case ScriptItem.tokenBAD:

 			   default:
				 error = true;
				 errors++;
 				 report.append("Software fault at line #" + textLineNum + "\n");
			} 
		}
		textLineNum++;			
	      dest = source.readLine();
	   }			

	} catch (IOException e) {

      } finally {
	   try {
		source.close();
	   	s.close();
	  } catch (IOException e) {
		// blah blah
	  }
	}
	
	if ((gotChute==false)||(gotName==false)) {
	   errors++;
	   report.append("Script must set both Name and # of Chutes\n\n");
	}

	report.append("LOADED : " + errors + " errors found.\n\n");
	return report.toString();
   }

   public  String getName() {
	return name;
   }

   public  ScriptItem getItem(int  index) {
	return (ScriptItem) chute.elementAt(index);
   } 
   
   private int toToken(String text) {
   	if (text.equals(ScriptItem.tokenTextName)) 	   return ScriptItem.tokenName;
	else if (text.equals(ScriptItem.tokenTextPut))     return ScriptItem.tokenPut;
	else if (text.equals(ScriptItem.tokenTextWait))    return ScriptItem.tokenWait;
	else if (text.equals(ScriptItem.tokenTextEndLoop)) return ScriptItem.tokenEndLoop;
	else if (text.equals(ScriptItem.tokenTextLoop))    return ScriptItem.tokenLoop;
	else if (text.equals(ScriptItem.tokenTextEnd))     return ScriptItem.tokenEnd;
	else if (text.equals(ScriptItem.tokenTextSync))    return ScriptItem.tokenSync;
	else if (text.equals(ScriptItem.tokenTextIndexPut)) return ScriptItem.tokenIndexPut;
	else if (text.equals(ScriptItem.tokenTextIndexSet)) return ScriptItem.tokenIndexSet;
	else if (text.equals(ScriptItem.tokenTextIndexInc)) return ScriptItem.tokenIndexInc;
	else if (text.equals(ScriptItem.tokenTextChute))    return ScriptItem.tokenChute;
	else if (text.equals(ScriptItem.tokenTextAssign))   return ScriptItem.tokenAssign;
	else if (text.equals(ScriptItem.tokenTextIndexWait))   return ScriptItem.tokenIndexWait;
	else return ScriptItem.tokenNone;
   }     

}
